/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <errno.h>

#include "config.h"
#include "color.h"
#include "MudServer.h"
#include "Utils.h"

#include "Error.h"
#include "Logger.h"
#include "Global.h"
#include "ParserFactory.h"

using namespace Chronos;
using namespace std;
    
template<> TaskQueue* Global<TaskQueue>::obj = 0;
template<> World* Global<World>::obj = 0;
template<> SessionManager* Global<SessionManager>::obj = 0;
template<> CommServer* Global<CommServer>::obj = 0;
template<> ItemDb* Global<ItemDb>::obj = 0;


MudServer::MudServer(unsigned int port, Database* database_)
    : database(database_)
    , taskQueue(Utils::getPulse())
    , world(eventServer, taskQueue)
    , parser(ParserFactory::create())
    , sessionManager(port, eventServer, parser.get(), *database)
    , commServer(eventServer)
{
    srandom(time(0));
    Global<TaskQueue>::set(&taskQueue);
    Global<ItemDb>::set(&itemDb);
    Global<World>::set(&world);
    Global<SessionManager>::set(&sessionManager);
    Global<CommServer>::set(&commServer);
    commServer.addChannel("gen", ANSI_BROWN);
    commServer.addChannel("sale", ANSI_BLUE);

    world.load();
}


void MudServer::loop(bool* stop) {
    const unsigned int PULSE_PERIOD = 1000000 / Config::PULSE_PER_SEC;
    u_int64_t pulseBeginTime = getCurrentTime();

    while (!*stop) {
        Utils::nextPulse();
       
        taskQueue.runNextTime();
        sessionManager.handlePulse();

        pulseBeginTime += PULSE_PERIOD;
        waitUntilTime(pulseBeginTime);
    }
}


u_int64_t MudServer::getCurrentTime() {
    struct timeval now;
    gettimeofday(&now, 0);
    u_int64_t now64 = now.tv_sec;
    now64 *= 1000000;
    now64 += now.tv_usec;
    return now64;
}


void MudServer::waitUntilTime(u_int64_t endTime) const {
    u_int64_t now = getCurrentTime();

    if (now > endTime) {
        LOG(WARN, "missed deadline (now=%llu, endTime=%llu", now, endTime);
    } else {
        u_int64_t delay = endTime - now;
        struct timeval waitTime;
        waitTime.tv_sec = delay / 1000000;
        waitTime.tv_usec = delay % 1000000;
        if (select(0, 0, 0, 0, &waitTime) != 0) {
            LOG(WARN, "select() returned error (%s)", strerror(errno));
        }
    }
}

